<?php

namespace app\admin\command;

use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;

use app\common\library\FSHelper;

use app\admin\model\Gateway;
use app\admin\model\Task;
use app\admin\model\TaskJob;

class AutoDialer extends Command {
    
    protected $fsCallInterval = 0; //1000; //单位：毫秒

    protected $gatewayCallInterval = 0; //单位：秒

    protected $fsHelper = null;

    protected function configure() {
        $this->setName('autodialer')
             ->setDescription('Dial outbound calls automatically');
    }
    
    protected function execute(Input $input, Output $output) {
        $loop = 0;
        $this->fsHelper = new FSHelper();
        while (1) {
            $loop++;
            $loop = ($loop >= PHP_INT_MAX) ? 1 : $loop;
            echo "\r\n------ AutoDialer Execute Loop: {$loop} ------\r\n";
            $result = false;
            if (! $this->fsHelper->connected()) {
                $this->fsHelper = null;
                $this->fsHelper = new FSHelper();
                if (! $this->fsHelper->connected()) {
                    usleep(1000 * 1000);
                    continue;
                }
            }
            try {
            $gateway_query_sql = "select fa_gateway.* from fa_gateway inner join fa_task_gateway_access on fa_task_gateway_access.gateway_id = fa_gateway.id inner join fa_task on (fa_task.state = '1' and fa_task.id = fa_task_gateway_access.tid) where fa_gateway.state = '1' and (((fa_gateway.call_interval > 0) and ((unix_timestamp(now()) - fa_gateway.last_call_time) > (fa_gateway.call_interval + RAND() * 10))) or (fa_gateway.call_interval <= 0)) group by fa_gateway.id";
            $gatewayList = db()->query($gateway_query_sql);
            foreach ($gatewayList as $one) {
                $result2 = false;
                $gateway = Gateway::get(['id' => $one['id']]);
                if (empty($gateway)) {
                    continue;
                }
                /*
                $gatewayCallInterval = max($gateway->call_interval, $this->gatewayCallInterval);
                if ($gatewayCallInterval > 0) {
                    $gatewayCallInterval = rand($gatewayCallInterval, $gatewayCallInterval + 10);
                } else {
                    $gatewayCallInterval = 0;
                }
                if ((time() - $gateway->last_call_time) <= $gatewayCallInterval) {
                    echo "Gateway [{$gateway->name}] execute too intervally\n";
                    continue;
                }
                */
                $result2 = $this->executeAutoDialByGateway($gateway);
                $result = $result ? $result : $result2;
                echo "Gateway [{$gateway->name}] execute result: ";
                var_dump($result2);
                if ($result2) {
                    usleep($this->fsCallInterval * 1000);
                }
            }
            //$this->fsHelper->disconnect();
            //$executeDuration = $this->updateTaskStatus();
            $executeDuration = 0;
            if (!$result) {
                $time_left = ($this->fsCallInterval - $executeDuration) > 0 ? ($this->fsCallInterval - $executeDuration) : 0;
                //echo $time_left . "\r\n";
                usleep($time_left * 1000);
            }
            } catch (Exception $e) {
                usleep(1000 * 1000);
            }
        }
        $this->fsHelper->disconnect();
    }

/*
    protected function updateTaskStatus() {
        $beginTime = time();
       
        $db = Db(); 
        $where = array();
        $where['state'] = array('=', '1');
        $taskList = Task::where($where)->order('id desc')->select();
        foreach ($taskList as $task) {
            $result = $db->query('select count(*) as count from fa_task_job where tid=?', [$task->id]);
            $task->total_count = $result[0]['count'];
            //$result = $db->query('select count(*) as count from fa_task_job where tid=? and called=1 and callstate in(\'success\', \'fail\')', [$task->id]);
            $result = $db->query('select count(*) as count from fa_task_job where tid=? and called=1', [$task->id]);
            $task->done_count = $result[0]['count'];
            if ($task->callback_type == 'ivr') {
                $result = $db->query('select count(*) as count from fa_task_job where tid=? and called=1 and ivrstate in(\'started\', \'finished\')', [$task->id]);
            } else {
                $result = $db->query('select count(*) as count from fa_task_job where tid=? and called=1 and queuestate in(\'recepted\')', [$task->id]);
            }
            $task->success_count = $result[0]['count'];
            $result = db()->query('select count(*) as count from limit_data where `realm`=? and `id`=?', ['task', $task->id]);
            $task_running_count = $result[0]['count'];
            $task->fail_count = $task->done_count - $task->success_count - $task_running_count;
            $task->fail_count = $task->fail_count > 0 ? $task->fail_count : 0;
            if ($task->total_count != 0) {
                $task->progress = sprintf('%.0f%%', $task->done_count * 100 / $task->total_count);
            } else {
                $task->progress = '0%';
            }
            //$where2 = array();
            //$where2['tid'] = $task->id;
            //$where2['called'] = 0;
            //$job = TaskJob::where($where2)->find();
            //if (empty($job)) {
            if ($task->progress == '100%') {
                //check for last task job finished
                if ($task_running_count <= 0) {
                    $task->state = 2;
                }
            }
            $task->save();
        }

        $executeDuration = time() - $beginTime;
        return $executeDuration;
    }
*/
    
    protected function updateGatewayConcurrency(&$gateway, $concurrency) {
        $gateway->concurrency = $concurrency;
        $gateway->save();
    }

    protected function checkGatewayAvailable(&$gateway) {
        $limit_query_sql = 'select count(*) as count from limit_data where `realm`=? and `id`=?';
        $list = db()->query($limit_query_sql, ['gateway', $gateway->username]);
        $concurrency = $list[0]['count'];
        $this->updateGatewayConcurrency($gateway, $concurrency);
        if ($gateway->max_concurrency == 0) {
            return true;
        } else {
            return ($gateway->max_concurrency > $concurrency);
        }
    }

    protected function increaseGatewayConcurrency(&$gateway, $step = 1) {
        $limit_query_sql = 'select count(*) as count from limit_data where `realm`=? and `id`=?';
        $list = db()->query($limit_query_sql, ['gateway', $gateway->username]);
        $concurrency = $list[0]['count'];
        $this->updateGatewayConcurrency($gateway, $concurrency + $step);
    }

    protected function executeAutoDialByGateway(&$gateway) {
        if (!$this->checkGatewayAvailable($gateway)) {
            return false;
        }

        $hasWhere = array();
        $hasWhere['gateway_id'] = $gateway->id;
        $where = array();
        $where['state'] = ['=', '1'];
        //$task = Task::hasWhere('taskGatewayAccess', $hasWhere)->where($where)->order('updatetime asc')->find();
        $taskList = Task::hasWhere('taskGatewayAccess', $hasWhere)->where($where)->order('id desc')->select();
        if (empty($taskList)) {
            return false;
        }
        $result = false;
        foreach ($taskList as $task) {
            //$task->updatetime = time();
            //$task->save();
            /*$count = $this->fsHelper->getAvailableAgentCountByTask($task->id);
            if ($count <= 0) continue;*/
            $result = $this->executeAutoDialByTask($task, $gateway);
            if ($result) break;
        }
        return $result;
    }

    protected function executeAutoDialByTask(&$task, &$gateway) {
        
        $where = array();
        $where['tid'] = $task->id;
        $where['called'] = 0;
        $job = TaskJob::where($where)->order('id asc')->find();
        if (empty($job)) {
            //$task->state = 2;
            //$task->save();
            return false;
        }
        return $this->executeAutoDialByJob($job, $task, $gateway);
    }

    protected function executeAutoDialByJob(&$job, &$task, &$gateway) {
        
        $fs_cmd = "bgapi originate {originate_timeout=180,absolute_codec_string='PCMA,PCMU',ignore_early_media=true,origination_caller_id_name='TASK{$task->id}',sip_h_X-TASKID='{$task->id}',origination_caller_id_number='JOB{$job->id}',gateway='{$gateway->username}',sip_h_X-Gateway='{$gateway->username}',sip_h_X-ConcurrencyLimit='{$gateway->max_concurrency}',sip_h_X-Gateway-Register='{$gateway->register}',sip_h_X-JOBID='{$job->id}',JOBID='{$job->id}',TASKID='{$task->id}',bypass_media=false}sofia/outcall/{$job->number}@127.0.0.1:7777 &transfer('{$task->id} xml callback')";
        //echo $fs_cmd;
        //echo "\r\n";
        $result = $this->fsHelper->api($fs_cmd);
        //var_dump($result);
        if ($result) {
            $job->called = 1;
            $job->calltime = time();
            $job->callgateway = $gateway->name;
            $job->save();
            $this->increaseGatewayConcurrency($gateway);
        }
        return $result;
        
    }

}
